<?php
/**
 * 框架核心类，以静态方式来使用
 * 在新版本中将App类定义为：应用上下文引用，而各个功能模块为独立类
 * 在每个功能模块类中都有定义若干静态方法供使用
 *
 * @version $Id: App.php 434 2014-04-01 08:49:23Z linsir123 $
 * @package Core
 */
class App
{
	/**
	 * @var 系统类列表，采用<<按需加载>>
	 * @static
	 */
	static $__classes = array(
		'BASE' => array(
			// 基础流程类
			'Config' => 'Config.php',
			'Controller' => 'Controller.php',
			'Dispatcher' => 'Dispatcher.php',
			'Request' => 'Request.php',

			// 服务提供类
			'LpRedis' => 'Nosql/Redis.php',
			'LpMemcache' => 'Nosql/Memcache.php',
			'LpMysql' => 'Mysql/Mysql.php',
			'ActiveRecord' => 'Mysql/ActiveRecord.php',
			'ActiveRelation' => 'Mysql/ActiveRelation.php',
			'ArCondition' => 'Mysql/Query/Condition.php',
			'ArCommand' => 'Mysql/Query/Command.php',
			'ArSelect' => 'Mysql/Query/Select.php',
			'View' => 'View/View.php',

			// 功能函数类
			'lpApi' => 'Common/Api.php',
			'lpDir' => 'Common/Dir.php',
			'lpFile' => 'Common/File.php',
			'lpString' => 'Common/String.php',
			'lpSystem' => 'Common/System.php',
			'lpUrl' => 'Common/Url.php'
		),
		'OTHER' => array()
	);

	/**
	 * @var APP过程加载的文件数
	 * @static
	 */
	static $__imports = array(__FILE__);

	/**
	 * @var APP实例对象集合
	 * @static
	 */
	static $__instances = array();

	/**
	 * @var 勾子
	 * @static
	 */
	static $__hooks = array(
		'end' => array(),
		'log' => array(),
	);

	/**
	 * @var 应用程序当前使用的目录，即子系统(目前定义的作用为在控制器和模板下目录)
	 * @static
	 */
	static $__subApp = null;

	/**
	 * 配置框架运行环境
	 *
	 * *、开启缓冲区
	 * *、配置[异常处理]
	 * *、配置[magic_quotes_gpc]
	 * *、配置[默认时区]
	 *
	 * @static
	 */
	private static function _initContext()
	{
		set_exception_handler(array('lpSystem', 'exceptionHandler'));

		///
		if (get_magic_quotes_gpc()) {
			$_GET = lpString::stripslashes($_GET);
			$_POST = lpString::stripslashes($_POST);
			$_COOKIE = lpString::stripslashes($_COOKIE);
		}

		///
		if (function_exists('date_default_timezone_set'))
			date_default_timezone_set('Asia/ShangHai');
	}

	/**
	 * 初始化应用相关信息
	 *
	 * *、[App]配置选项
	 * *、[App]报错模式
	 * *、[App]自定义类注册
	 *
	 * @static
	 */
	private static function _initApp()
	{
		self::getConf()->init();

		///
		if (! self::O('main.debug'))
			set_error_handler(array('lpSystem', 'errorHandler'), E_ALL);

		///
		self::registerClassByDir(APP_DIR . "Lib/", array('Init'), 1);

		///
		lpSystem::hCharset(self::O('main.charset.html'));
	}

	/**
	 * 初始化应用程序
	 *
	 * @param string $route 路由信息
	 *
	 * @static
	 */
	static function run($route = '')
	{
		self::_initContext();
		self::_initApp();

		///
		$tmp = self::getRequest()->parse($route);
		list($controllerName, $actionName) = $tmp;

		///
		self::getDispatcher()->gotoCtrl($controllerName, $actionName);
		self::end();
	}

	/**
	 * 结束应用并输出结果
	 *
	 * @static
	 */
	static function end()
	{
		if (self::isSingleton('View'))
			self::getView()->render();

		///
		self::activeHook('end');

		///
		exit();
	}

	/**
	 * 配置类自动加载
	 *
	 * @param string $class 类名
	 *
	 * @static
	 */
	static function autoLoad($class)
	{
		$classes = self::$__classes;
		if (isset($classes['BASE'][$class]))
			self::import($classes['BASE'][$class]);
		elseif (isset($classes['OTHER'][$class]))
			self::import($classes['OTHER'][$class], null);
	}

	/**
	 * 注册类以便在使用时可以自动加载
	 *
	 * @param string $class 类名
	 * @param string $file 类对应的文件物理地址
	 *
	 * @static
	 */
	static function registerClass($class, $file)
	{
		self::$__classes['OTHER'][$class] = $file;
	}

	/**
	 * 注册勾子
	 *
	 * @param string $hook 勾子名称
	 * @param callback $callback 回调
	 *
	 * @static
	 */
	static function registerHook($hook, $callback)
	{
		if (array_key_exists($hook, App::$__hooks) && is_callable($callback))
			App::$__hooks[$hook][] = $callback;
	}

	/**
	 * 执行某个勾子
	 *
	 * @param string $hook
	 * @param mixed $params
	 * @param bool $return
	 */
	static function activeHook($hook, $params = null, $return = false)
	{
		$needArgs = ! empty($params);
		$list = isset(App::$__hooks[$hook]) ?
			App::$__hooks[$hook] : array();

		///
		foreach ($list as $callback) {
			$args = $needArgs ? array($params) : array();
			$params = call_user_func_array($callback, $args);
		}

		///
		if ($return)
			return $params;
	}

	/**
	 * 注册类以便在使用时可以自动加载,对某个目录内的所有文件进行注册操作
	 *
	 * @param string $dir 类对应的文件目录物理地址
	 * @param array $excludes 不需要进行注册的文件的排除列表
	 * @param int $excludeType 排除方式(0不处理, 1进行加载)
	 *
	 * @static
	 */
	static function registerClassByDir($dir, $excludes = array(), $excludeType = 0)
	{
		if (file_exists($dir) && is_dir($dir)) {
			$files = glob($dir . "*.php");

			///
			foreach ($files as $file) {
				$fileName = basename($file);
				$fileName = substr($fileName, 0, strpos($fileName, '.'));
				if (in_array($fileName, $excludes)) {
					if ($excludeType == 1)
						self::import($file, null);
				}
				else
					self::registerClass($fileName, $file);
			}
		}
	}

	/**
	 * 加载文件
	 *
	 * @param string $file 文件名
	 * @param string $dir 文件所在的目录
	 * @param bool $throw 若文件不存在是否抛出异常
	 *
	 * @return mixed 若文件不存在返回`false`;否则返回`require`信息
	 * @static
	 */
	static function import($file, $dir = CORE_DIR, $throw = true)
	{
		$file = $dir . $file;
		if (! in_array($file, self::$__imports)) {
			array_push(self::$__imports, $file);

			///
			if (file_exists($file))
				return require($file);

			///
			if ($throw)
				throw new Exception("[APP] 文件不存在({$file})");
		}

		return false;
	}

	/**
	 * 判断某个类是否实例化
	 *
	 * @param string $class 类名
	 * @param int $key 同名类时,区间的键值
	 *
	 * @return bool
	 * @static
	 */
	static function isSingleton($class, $key = 0)
	{
		return isset(self::$__instances[$class][$key]);
	}

	/**
	 * 取得某个类的实例化对象
	 *
	 * @param string $class 类名
	 * @param array $params 类实例化时需要的参数
	 * @param int $key 同一个类的某个实例
	 *
	 * @return mixed
	 * @static
	 */
	static function singleton($class, $params = array(), $key = 0)
	{
		if (self::isSingleton($class, $key))
			return self::$__instances[$class][$key];

		///
		if (! class_exists($class))
			throw new Exception("[APP] 类不存在({$class})");

		///
		$singleton = self::newInstance($class, $params);
		self::$__instances[$class][$key] = $singleton;

		return $singleton;
	}

	/**
	 * 返回某个类的实例
	 *
	 * @param string $class 类名
	 * @param array $params 类实例化时需要的参数
	 *
	 * @return mixed
	 * @static
	 */
	static function newInstance($class, $params = array())
	{
		if (count($params) == 0)
			return new $class;

		///
		$ref = new ReflectionClass($class);

		return $ref->newInstanceArgs($params);
	}

	/**
	 * 取得[Mysql]对象
	 *
	 * @param int 可根据指定的配置生成相应的数据源
	 *
	 * @return mixed
	 * @static
	 */
	static function getMysql($dsnNumber = 0)
	{
		return self::singleton('LpMysql', array($dsnNumber), $dsnNumber);
	}

	/**
	 * 取得[Memcache]对象
	 *
	 * @static
	 */
	static function getMc()
	{
		return self::singleton('LpMemcache');
	}

	/**
	 * 取得[Redis]对象
	 *
	 * @static
	 */
	static function getRedis($dsnNumber = 0)
	{
		return self::singleton('LpRedis', array($dsnNumber), $dsnNumber);
	}

	/**
	 * 取得配置对象
	 *
	 * @static
	 */
	static function getConf()
	{
		return self::singleton('Config');
	}

	/**
	 * 取得视图对象
	 *
	 * @static
	 */
	static function getView()
	{
		return self::singleton('View');
	}

	/**
	 * 取得消息调度器对象
	 *
	 * @static
	 */
	static function getDispatcher()
	{
		return self::singleton('Dispatcher');
	}

	/**
	 * 取得HTTP请求信息处理对象
	 *
	 * @static
	 */
	static function getRequest()
	{
		return self::singleton('Request');
	}

	/**
	 * 返回一个模型对象
	 *
	 * @param string $name 模型名
	 * @param mixed $fieldId 用于数据分表的字段值(单个或组合)，并且会传递到构造函数中
	 * @param string $sub 用于数据分层或归类(经/纬度)
	 * @param string $prefix 用于避免类名重复冲突
	 * @param int $key 同一个类的某个实例(0则返回某个实例, 非0则为某个单例)
	 *
	 * @return mixed
	 * @static
	 */
	static function M($name, $fieldId = null, $sub = null, $prefix = 'model', $key = 1)
	{
		static $list = array();

		///
		$class = $prefix . $name;
		if (! in_array($class, $list)) {
			array_push($list, $class);

			///
			$file = $name . '.php';
			$dir = APP_DIR . 'Model/';
			if (! empty($sub))
				$dir .= ucfirst($sub) . DS;

			self::import($file, $dir);
		}

		///
		$params = empty($fieldId) ? array() : array($fieldId);
		if ($key === 0)
			return self::newInstance($class, $params);

		return self::singleton($class, $params, $key);
	}

	/**
	 * 加载一个第三方入口文件
	 *
	 * @param string $name 第三方文件名
	 *
	 * @static
	 */
	static function V($name)
	{
		static $list = array();

		///
		if (! in_array($name, $list)) {
			array_push($list, $name);

			///
			$file = 'Vendors/' . $name . '.php';
			$dir = file_exists(APP_DIR . $file) ? APP_DIR : BASE_DIR;
			self::import($file, $dir);
		}
	}

	/**
	 * 返回一个组件对象
	 * // 需要传入类构造函数的参考，可以从第二个参数开始
	 *
	 * @param string $name 组件名
	 *
	 * @return mixed
	 * @static
	 */
	static function C($name)
	{
		$file = 'Components/' . $name . '.php';
		self::import($file, BASE_DIR);

		///
		$class = 'com' . $name;
		$params = func_get_args();
		array_shift($params);

		///
		return self::singleton($class, $params);
	}

	/**
	 * 取得配置选项
	 *
	 * @param string $key 选项的键值
	 *
	 * @return mixed
	 * @static
	 */
	static function O($key, $package = null)
	{
		return self::getConf()->get($key, $package);
	}

	/**
	 * 日志处理
	 *
	 * @param mixed $content 需要记录的日志内容
	 * @param string $relativeDir 相对目录名
	 * @param int $cutting 文件切割方式(false默认，true按天切割)
	 *
	 * @static
	 */
	static function log($content, $relativeDir = 'debug', $cutting = true)
	{
		$ip = Request::ip();
		$time = date('Y-m-d H:i:s');
		$route = self::getRequest()->route;
		$method = $_SERVER['REQUEST_METHOD'];
		$content = is_string($content) ? $content : var_export($content, true);
		$content = sprintf("%s - %s - \"%s /%s\" %s \r\n", $time, $ip, $method, $route, $content);

		///
		$dir = CACHE_DIR . $relativeDir;
		$file = $dir . sprintf("/%slog.txt", $cutting ? (date("Y_m_d") . "-") : "");
		$maxSize = intval(self::O('main.logSize'));

		///
		self::activeHook('log', array(
			$content,
			$relativeDir,
		));

		///
		lpDir::make($dir);
		lpFile::write($file,
			$content,
			"a+",
			$maxSize);
	}

	/**
	 * 返回应用上下文信息
	 * // 包含：[使用的内存,运行时间,数据库信息]
	 *
	 * @return array 运行信息
	 * @static
	 */
	static function info($return = true)
	{
		/// 回收
		return array();
	}
}
